/*
 * BLLib.Xml.XmlTag
 * Andy Tidball
 * 
 * Project: Black Lightning General Library
 * Copyright: GNU General Public License
 */

using System;
using System.Collections.Generic;
using System.Xml;

namespace BLLib.Xml {
	/// <summary>
	/// Represents a single tag within an XML document.
	/// </summary>
	public class XmlTag {
		/// <summary>
		/// Parses through the current element of the stream and builds a list of its attributes.
		/// </summary>
		/// <param name="XmlStream">The stream to parse the current element of.</param>
		/// <returns>A list of name/value pairs representing the attributes of the current element.</returns>
		private static Dictionary<string, string> ParseAttributes(XmlReader XmlStream) {
			Dictionary<string, string> Attributes = new Dictionary<string, string>();

			// move to the first attribute of the current element
			XmlStream.MoveToFirstAttribute();
			do {
				// add this attribute to the list
				if (XmlStream.NodeType == XmlNodeType.Attribute) {
					Attributes.Add(XmlStream.Name, XmlStream.Value);
				}
			} while (XmlStream.MoveToNextAttribute());

			// return the generated list
			return Attributes;
		}

		// member data
		private string                     _Name       = null;         // the name of the tag
		private Dictionary<string, string> _Attributes = null;         // the tag's attributes
		private string                     _Text       = string.Empty; // the tag's text
		private List<XmlTag>               _Children   = null;         // the tag's child tags

		/// <summary>
		/// Creates a new XmlTag.
		/// </summary>
		/// <param name="Name">The tag's name.</param>
		public XmlTag(string Name)
			: this(Name, string.Empty) {
		}

		/// <summary>
		/// Creates a new XmlTag.
		/// </summary>
		/// <param name="Name">The tag's name.</param>
		/// <param name="Text">The tag's value text.</param>
		public XmlTag(string Name, string Text)
			: this(Name, new Dictionary<string, string>(), Text) {
		}

		/// <summary>
		/// Creates a new XmlTag.
		/// </summary>
		/// <param name="Name">The tag's name.</param>
		/// <param name="Attributes">A collection of attributes for the tag (a zero-length list for no attributes).</param>
		public XmlTag(string Name, Dictionary<string, string> Attributes)
			: this(Name, Attributes, string.Empty) {
		}

		/// <summary>
		/// Creates a new XmlTag.
		/// </summary>
		/// <param name="Name">The tag's name.</param>
		/// <param name="Children">A list of child tags that are inside this one.</param>
		public XmlTag(string Name, List<XmlTag> Children)
			: this(Name, new Dictionary<string, string>(), Children) {
		}

		/// <summary>
		/// Creates a new XmlTag.
		/// </summary>
		/// <param name="Name">The tag's name.</param>
		/// <param name="Attributes">A collection of attributes for the tag (a zero-length list for no attributes).</param>
		/// <param name="Text">The tag's value text.</param>
		public XmlTag(string Name, Dictionary<string, string> Attributes, string Text) {
			_Name       = Name;
			_Attributes = Attributes;
			_Text       = Text;
			_Children   = new List<XmlTag>();
		}

		/// <summary>
		/// Creates a new XmlTag.
		/// </summary>
		/// <param name="Name">The tag's name.</param>
		/// <param name="Attributes">A collection of attributes for the tag (a zero-length list for no attributes).</param>
		/// <param name="Children">A list of child tags that are inside this one.</param>
		public XmlTag(string Name, Dictionary<string, string> Attributes, List<XmlTag> Children) {
			_Name       = Name;
			_Attributes = Attributes;
			_Text       = string.Empty;
			_Children   = Children;
		}

		/// <summary>
		/// Creates a new XmlTag by reading it from the given stream.
		/// </summary>
		/// <param name="XmlStream">The XML stream to read the tag from.</param>
		internal XmlTag(XmlReader XmlStream) {
			// move to the current element
			XmlStream.MoveToElement();

			// record the tag's name
			_Name = XmlStream.Name;

			// parse out the tag's attributes
			_Attributes = XmlTag.ParseAttributes(XmlStream);

			// read in subtags
			_Children = new List<XmlTag>();
			XmlStream.MoveToElement();
			bool Done = XmlStream.IsEmptyElement;
			while (!Done) {
				XmlStream.Read();

				// decide what to do based on the next thing
				switch (XmlStream.NodeType) {
					case XmlNodeType.Text: {
							// the text value of the tag
							_Text = XmlStream.Value;
							break;
						}
					case XmlNodeType.Element: {
							// a subtag
							_Children.Add(new XmlTag(XmlStream));
							break;
						}
					case XmlNodeType.EndElement: {
							// the closing tag
							if (XmlStream.Name == _Name) {
								Done = true;
							}
							break;
						}
				}
			}
		}

		/// <summary>
		/// Gets the name of the tag.
		/// </summary>
		public string Name {
			get {
				return _Name;
			}
		}

		/// <summary>
		/// Gets a collection of attributes associated with the tag.
		/// </summary>
		public Dictionary<string, string> Attributes {
			get {
				return _Attributes;
			}
		}

		/// <summary>
		/// Gets the value text within the tag.
		/// </summary>
		public string Text {
			get {
				return _Text;
			}
		}

		/// <summary>
		/// Gets a list of subtags contained within the tag.
		/// </summary>
		public List<XmlTag> Children {
			get {
				return _Children;
			}
		}

		/// <summary>
		/// Saves the tag to the given XML stream.
		/// </summary>
		/// <param name="XmlStream">The stream to save the tag to.</param>
		internal void Save(XmlWriter XmlStream) {
			// start the tag we're saving
			XmlStream.WriteStartElement(Name);

			// write out each of the tag's elements
			foreach (KeyValuePair<string, string> Pair in Attributes) {
				XmlStream.WriteAttributeString(Pair.Key, Pair.Value);
			}

			// if we have a text value, write that out
			if (_Text != null) {
				XmlStream.WriteString(Text);
			}

			// if we have children, loop over each and save it to the stream
			foreach (XmlTag T in Children) {
				T.Save(XmlStream);
			}

			// end the tag
			XmlStream.WriteEndElement();
		}
	}
}
